/*
 * Copyright 2010-2012 Susanta Tewari. <freecode4susant@users.sourceforge.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
* TableColumnChooser.java
*
* Created on December 29, 2007, 10:16 AM
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
 */
package javautil.swing.table;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.lang.ref.WeakReference;
import java.util.Arrays;

import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;

/**
 *
 * @author stewari1
 */
public class TableColumnChooser extends MouseAdapter implements ActionListener {


    // client property used to specify fixed columns. value is of type int[] which is sorted
    // fixed column = columns that can't be toggled

    /** Field description */
    private static final String FIXED_COLUMNS = "FixedColumns";    // NOI18N


    // client property used to pass column index to actionlistener

    /** Field description */
    private static final String COLUMN_INDEX = "ColumnIndex";


    // client property used to pass jtable to actionlistener

    /** Field description */
    private static final String JTABLE = "JTable";

    /* -------------------------------------------------[ Singleton ]--------------------------------------------------- */

    /** Field description */
    private static WeakReference ref = null;    // favour gc

    /**
     * Constructs ...
     */
    private TableColumnChooser() {}

    /**
     * Method description
     *
     * @param model description
     * @param modelCol description
     *
     * @return description
     */
    private boolean isVisibleColumn(TableColumnModel model, int modelCol) {

        for (int i = 0; i < model.getColumnCount(); i++) {

            if (model.getColumn(i).getModelIndex() == modelCol) return true;
        }

        return false;
    }

    /* -----------------------------------[ MouseListener ]------------------------------------------ */
    public void mouseReleased(MouseEvent me) {

        if (!SwingUtilities.isRightMouseButton(me)) return;

        JTableHeader header          = (JTableHeader) me.getComponent();
        TableModel tableModel        = header.getTable().getModel();
        TableColumnModel columnModel = header.getTable().getColumnModel();
        int fixedColumns[]           = (int[]) header.getTable().getClientProperty(FIXED_COLUMNS);

        if (fixedColumns == null) fixedColumns = new int[] { 0 };

        JPopupMenu popup = new JPopupMenu();

        for (int i = 0; i < tableModel.getColumnCount(); i++) {

            JCheckBoxMenuItem item = new JCheckBoxMenuItem(tableModel.getColumnName(i));

            item.setSelected(isVisibleColumn(columnModel, i));
            item.setEnabled(Arrays.binarySearch(fixedColumns, i) < 0);
            item.putClientProperty(COLUMN_INDEX, new Integer(i));
            item.putClientProperty(JTABLE, header.getTable());
            item.addActionListener(this);
            popup.add(item);
        }

        popup.show(header, me.getX(), me.getY());
    }

    /* ------------------------------------[ ActionListener ]-------------------------------------- */
    public void actionPerformed(ActionEvent ae) {

        JMenuItem item               = (JMenuItem) ae.getSource();
        Integer columnIndex          = (Integer) item.getClientProperty(COLUMN_INDEX);
        JTable table                 = (JTable) item.getClientProperty(JTABLE);
        TableColumnModel columnModel = table.getColumnModel();

        if (!item.isSelected()) {

            for (int i = 0; i < columnModel.getColumnCount(); i++) {

                TableColumn col = columnModel.getColumn(i);

                if (col.getModelIndex() == columnIndex.intValue()) {

                    columnModel.removeColumn(col);

                    return;
                }
            }

        } else
            table.addColumn(new TableColumn(columnIndex.intValue()));
    }

    /**
     * Method description
     *
     * @return description
     */
    private static TableColumnChooser getInstance() {

        if ((ref == null) || (ref.get() == null)) ref = new WeakReference(new TableColumnChooser());

        return (TableColumnChooser) ref.get();
    }

    /* -------------------------------------------------[ Usage ]--------------------------------------------------- */

    /**
     * Method description
     *
     * @param table description
     */
    private static void install(JTable table) {
        table.getTableHeader().addMouseListener(getInstance());
    }

    public static void uninstall(JTable table) {
        table.getTableHeader().removeMouseListener(getInstance());
    }

    /* -------------------------------------------------[ Utilities ]--------------------------------------------------- */

    /**
     * Method description
     *
     * @param columnModel description
     * @param modelColumnIndexes description
     */
    private static void hideColumns(TableColumnModel columnModel, int modelColumnIndexes[]) {

        TableColumn column[] = new TableColumn[modelColumnIndexes.length];

        for (int i = 0, j = 0; i < columnModel.getColumnCount(); i++) {

            TableColumn col = columnModel.getColumn(i);

            if (col.getModelIndex() == modelColumnIndexes[j]) {

                column[j++] = col;

                if (j >= modelColumnIndexes.length) break;
            }
        }

        for (int i = 0; i < column.length; i++) {

            columnModel.removeColumn(column[i]);
        }
    }

    public static void main(String[] args) {

        JTable table = new JTable(new DefaultTableModel(new String[] {
            "Name", "Type", "Modified", "Size", "Ratio", "Packed", "CRC", "Attributes", "Path"
        }, 10));


        // hide some columns initially
        TableColumnChooser.hideColumns(table.getColumnModel(), new int[] { 2, 4, 5, 6, 7 });


        // don't allow user to hide "Name" and "Path" columns
        table.putClientProperty(TableColumnChooser.FIXED_COLUMNS, new int[] { 0, 8 });
        TableColumnChooser.install(table);

        JFrame frame = new JFrame();

        frame.getContentPane().add(table);
        frame.pack();
        frame.setVisible(true);
    }
}
